Skip to content

fix: move artist redirect to live client tool handling#1642

Open
arpitgupta1214 wants to merge 1 commit intotestfrom
codex/server-copy-client-redirect-chat
Open

fix: move artist redirect to live client tool handling#1642
arpitgupta1214 wants to merge 1 commit intotestfrom
codex/server-copy-client-redirect-chat

Conversation

@arpitgupta1214
Copy link
Copy Markdown
Collaborator

@arpitgupta1214 arpitgupta1214 commented Apr 6, 2026

Summary

  • remove the frontend artist message copy flow
  • handle redirect from the live create artist tool via useChat onToolCall/onFinish
  • make chat transport resolve the API override URL at request time and prefer the api query param when present

Testing

  • manual verification only for the final inline transport helper change
  • earlier focused transport test passed before helper inlining

Summary by cubic

Moves artist-creation redirect to client-side handling using the live completion event, removing the message-copy flow and redirecting after streaming completes. Also keeps the ?api override preferred and resolved per request.

  • Refactors
    • Handle redirect via useVercelChat: capture data-redirect path in onData, apply in onFinish, and reset on errors.
    • Remove useCreateArtistTool and lib/messages/copyMessages; simplify success UI to “Artist created successfully”.
    • Drop newRoomId from CreateArtistResult.

Written for commit 7602294. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor
    • Streamlined artist creation process by consolidating multiple processing steps, resulting in faster completion and reduced visual clutter
    • Simplified success messaging to provide clearer, more direct feedback to users
    • Enhanced chat system integration to handle post-creation navigation automatically
    • Overall workflow optimization for improved efficiency and responsiveness

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-chat Error Error Apr 6, 2026 7:15pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cefa3696-f6de-43ee-b40b-a7f56948abc5

📥 Commits

Reviewing files that changed from the base of the PR and between 4887ee5 and 7602294.

⛔ Files ignored due to path filters (1)
  • types/createArtistResult.ts is excluded by none and included by none
📒 Files selected for processing (4)
  • components/VercelChat/tools/CreateArtistToolResult.tsx
  • hooks/useCreateArtistTool.ts
  • hooks/useVercelChat.ts
  • lib/messages/copyMessages.ts
💤 Files with no reviewable changes (2)
  • hooks/useCreateArtistTool.ts
  • lib/messages/copyMessages.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • hooks/useVercelChat.ts
  • components/VercelChat/tools/CreateArtistToolResult.tsx

📝 Walkthrough

Walkthrough

This pull request refactors the artist creation workflow by removing client-side message-copying logic (useCreateArtistTool hook and copyMessages API function) and shifting redirect handling to the server via a new onData handler in useVercelChat that processes redirect paths during streaming.

Changes

Cohort / File(s) Summary
Component Simplification
components/VercelChat/tools/CreateArtistToolResult.tsx
Removed useCreateArtistTool hook usage; simplified GenericSuccess to always render static success message without conditional processing or error state handling.
Hook Removals
hooks/useCreateArtistTool.ts, lib/messages/copyMessages.ts
Deleted entire useCreateArtistTool hook (91 lines) managing async artist creation workflow including message copying, room redirection, and error tracking; removed copyMessages API client function (58 lines) that handled POST requests to copy messages between rooms.
Redirect Flow Migration
hooks/useVercelChat.ts
Added server-driven redirect handling via redirectPathRef, new dataPartSchemas for parsing redirect data parts, onData handler to capture redirect paths, and updated onFinish to apply redirects via window.history.replaceState after streaming completes.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Reviewers

  • sweetmantech

Poem

🎨 Hook and function gracefully fade away,
As server-side redirects seize the day,
Cleaner flows through data streams ascend,
Client logic bows—let the server send!

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Solid & Clean Code ✅ Passed PR demonstrates strong SOLID principles adherence by eliminating multi-responsibility useCreateArtistTool hook, consolidating business logic server-side, reducing frontend complexity, and applying DRY principle effectively.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/server-copy-client-redirect-chat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3445471abc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +16 to +17
if (apiParam) {
return new URL(apiParam).toString().replace(/\/+$/, "");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block untrusted api query overrides

Accepting any ?api= URL here allows a crafted link to reroute authenticated client requests to an attacker-controlled origin. Chat requests attach a bearer token (useChatTransport adds Authorization), so a logged-in user who opens such a URL and sends a message can leak credentials cross-origin. Restrict this override to trusted hosts (or dev-only) before using it as the API base.

Useful? React with 👍 / 👎.

Comment on lines +201 to +204
if (pendingRedirectPathRef.current) {
const redirectPath = pendingRedirectPathRef.current;
pendingRedirectPathRef.current = null;
window.history.replaceState({}, "", redirectPath);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh conversations before applying redirect path

This redirect flow updates the URL but never refreshes the conversations query, so after a create_new_artist redirect the sidebar can continue showing stale chat data until a manual refresh/focus-triggered refetch. The previous create-artist flow explicitly refetched conversations; without an equivalent invalidation here, recent chats can lag behind the new room state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/api/getClientApiBaseUrl.ts (1)

11-24: ⚠️ Potential issue | 🟠 Major

Keep the sessionStorage fallback when ?api= is malformed.

If new URL(apiParam) throws, the outer catch skips Lines 20-25 and falls straight back to the default base URL. That means an invalid query param disables an existing stored override instead of ignoring the bad param and continuing with the session fallback. Split query parsing and storage access into separate try/catch blocks.

🩹 Suggested fix
   if (typeof window !== "undefined") {
-    try {
-      const apiParam = new URLSearchParams(window.location.search).get("api");
-      if (apiParam === "clear") {
-        return defaultApiBaseUrl;
-      }
-
-      if (apiParam) {
-        return new URL(apiParam).toString().replace(/\/+$/, "");
-      }
-
-      const storedApiOverride = window.sessionStorage.getItem(
-        API_OVERRIDE_STORAGE_KEY,
-      );
-      if (storedApiOverride) {
-        return storedApiOverride.replace(/\/+$/, "");
-      }
-    } catch {
-      // Ignore storage failures and fall back to default.
+    const apiParam = new URLSearchParams(window.location.search).get("api");
+    if (apiParam === "clear") {
+      return defaultApiBaseUrl;
+    }
+
+    if (apiParam) {
+      try {
+        return new URL(apiParam).toString().replace(/\/+$/, "");
+      } catch {
+        // Ignore invalid query overrides and continue to sessionStorage.
+      }
+    }
+
+    try {
+      const storedApiOverride = window.sessionStorage.getItem(
+        API_OVERRIDE_STORAGE_KEY,
+      );
+      if (storedApiOverride) {
+        return storedApiOverride.replace(/\/+$/, "");
+      }
+    } catch {
+      // Ignore storage failures and fall back to default.
     }
   }

As per coding guidelines, lib/**/*.ts: For utility functions, ensure: Proper error handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/getClientApiBaseUrl.ts` around lines 11 - 24, The current parsing of
the ?api= query param uses new URL(apiParam) inside a try/catch that, on
failure, bypasses the sessionStorage fallback; split the logic so URL parsing
and sessionStorage access have independent error handling: first safely read
apiParam and handle the "clear" case, then try to construct new URL(apiParam) in
its own try/catch and only return a normalized URL if parsing succeeds, but if
parsing throws do not return default immediately — proceed to retrieve
API_OVERRIDE_STORAGE_KEY from window.sessionStorage (its own try/catch) and
return its normalized value if present, otherwise fall back to
defaultApiBaseUrl; references: apiParam, new URL(apiParam),
API_OVERRIDE_STORAGE_KEY, storedApiOverride.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/VercelChat/ToolComponents.tsx`:
- Around line 346-349: The code unconditionally JSON.parse's dynamic-tool output
into result, which throws for empty/non-JSON redirect outputs; change the logic
so parsing is guarded: either defer JSON.parse until after you inspect the tool
branch (e.g., check for redirect tool type/name) or wrap the parse of (output as
CallToolResult).content[0].text in a safe path that verifies content exists and
is valid JSON (or try/catch) before assigning to result; adjust the logic around
the variables type, output, CallToolResult and the redirect handling so lines
handling redirect (around the current branching at/after where result is used)
never see an unguarded JSON.parse (also apply same guard to the code at the
other occurrence noted).

In `@hooks/useVercelChat.ts`:
- Around line 189-196: The pending redirect handling inside onToolCall currently
only completes if refetchCredits() resolves, so a rejected refetch can leave
pendingRedirectPathRef.current set and prevent navigation; update onToolCall to
always perform the redirect navigation and reset pendingRedirectPathRef.current
regardless of refetchCredits() outcome by moving the redirect nav/cleanup into a
finally block or by catching errors from refetchCredits() and then executing the
redirect and clearing pendingRedirectPathRef; refer to onToolCall,
pendingRedirectPathRef, and refetchCredits() in the change.

In `@lib/api/getClientApiBaseUrl.ts`:
- Around line 11-18: The current getClientApiBaseUrl logic allows any ?api=
absolute URL which can redirect production traffic; change it to only accept
overrides if the origin is trusted or when running in a non-production/dev mode.
Specifically, update the code around apiParam (in getClientApiBaseUrl) to: 1)
parse apiParam and validate its origin against a hardcoded allowlist of trusted
origins or check a runtime feature flag/ENV (e.g., NODE_ENV !== 'production' or
an ALLOW_API_OVERRIDE flag) before returning it; 2) if validation fails, fall
back to defaultApiBaseUrl; and 3) keep the trimming of trailing slashes but do
not accept raw hostnames/paths without explicit allowlist/flag; also ensure any
persistent use in providers/ApiOverrideSync.tsx and hooks/useChatTransport.ts
respects the same validation function to avoid leaking requests to untrusted
origins.

---

Outside diff comments:
In `@lib/api/getClientApiBaseUrl.ts`:
- Around line 11-24: The current parsing of the ?api= query param uses new
URL(apiParam) inside a try/catch that, on failure, bypasses the sessionStorage
fallback; split the logic so URL parsing and sessionStorage access have
independent error handling: first safely read apiParam and handle the "clear"
case, then try to construct new URL(apiParam) in its own try/catch and only
return a normalized URL if parsing succeeds, but if parsing throws do not return
default immediately — proceed to retrieve API_OVERRIDE_STORAGE_KEY from
window.sessionStorage (its own try/catch) and return its normalized value if
present, otherwise fall back to defaultApiBaseUrl; references: apiParam, new
URL(apiParam), API_OVERRIDE_STORAGE_KEY, storedApiOverride.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3e65dcd8-1e7a-4a6a-b542-60b307c63ba0

📥 Commits

Reviewing files that changed from the base of the PR and between 87c2d58 and 3445471.

📒 Files selected for processing (8)
  • components/VercelChat/ToolComponents.tsx
  • components/VercelChat/tools/CreateArtistToolResult.tsx
  • hooks/useChatTransport.ts
  • hooks/useCreateArtistTool.ts
  • hooks/useVercelChat.ts
  • lib/api/getClientApiBaseUrl.ts
  • lib/messages/copyMessages.ts
  • providers/VercelChatProvider.tsx
💤 Files with no reviewable changes (2)
  • lib/messages/copyMessages.ts
  • hooks/useCreateArtistTool.ts

Comment on lines +346 to +349
const result =
type === "dynamic-tool"
? JSON.parse((output as CallToolResult).content[0].text)
: output;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent pre-branch parse crashes for redirect dynamic-tool outputs.

result is parsed before tool-specific branching, so a non-JSON/empty redirect output can throw before Line 441 is reached.

💡 Proposed fix
 export function getToolResultComponent(part: ToolUIPart | DynamicToolUIPart) {
   const { toolCallId, output, type } = part;
-  const result =
-    type === "dynamic-tool"
-      ? JSON.parse((output as CallToolResult).content[0].text)
-      : output;
   const toolName = getToolOrDynamicToolName(part);
+
+  if (toolName === "redirect") {
+    return null;
+  }
+
+  const result =
+    type === "dynamic-tool"
+      ? (() => {
+          const text = (output as CallToolResult)?.content?.[0]?.text;
+          if (!text) return output;
+          try {
+            return JSON.parse(text);
+          } catch {
+            return output;
+          }
+        })()
+      : output;
   const isSearchWebTool = toolName === "search_web";
   const isDeepResearchTool = toolName === "web_deep_research";
@@
-  } else if (toolName === "redirect") {
-    return null;
   } else if (toolName === "get_youtube_channels") {

Also applies to: 441-442

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/VercelChat/ToolComponents.tsx` around lines 346 - 349, The code
unconditionally JSON.parse's dynamic-tool output into result, which throws for
empty/non-JSON redirect outputs; change the logic so parsing is guarded: either
defer JSON.parse until after you inspect the tool branch (e.g., check for
redirect tool type/name) or wrap the parse of (output as
CallToolResult).content[0].text in a safe path that verifies content exists and
is valid JSON (or try/catch) before assigning to result; adjust the logic around
the variables type, output, CallToolResult and the redirect handling so lines
handling redirect (around the current branching at/after where result is used)
never see an unguarded JSON.parse (also apply same guard to the code at the
other occurrence noted).

Comment on lines +189 to +196
onToolCall: async ({ toolCall }) => {
if ("toolName" in toolCall && toolCall.toolName === "redirect") {
const path = (toolCall.input as { path?: string } | undefined)?.path;
if (path?.startsWith("/")) {
pendingRedirectPathRef.current = path;
}
}
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make redirect cleanup independent of refetchCredits() success.

Right now, a rejected refetchCredits() prevents both redirect execution and ref reset, so stale redirect state can leak into a later response.

💡 Proposed fix
       onToolCall: async ({ toolCall }) => {
         if ("toolName" in toolCall && toolCall.toolName === "redirect") {
           const path = (toolCall.input as { path?: string } | undefined)?.path;
-          if (path?.startsWith("/")) {
+          if (path && /^\/(?!\/)/.test(path)) {
             pendingRedirectPathRef.current = path;
           }
         }
       },
       onFinish: async () => {
-        // Update credits after AI response completes
-        await refetchCredits();
-
-        if (pendingRedirectPathRef.current) {
-          const redirectPath = pendingRedirectPathRef.current;
-          pendingRedirectPathRef.current = null;
-          window.history.replaceState({}, "", redirectPath);
-        }
+        const redirectPath = pendingRedirectPathRef.current;
+        pendingRedirectPathRef.current = null;
+
+        try {
+          // Update credits after AI response completes
+          await refetchCredits();
+        } finally {
+          if (redirectPath && /^\/(?!\/)/.test(redirectPath)) {
+            window.history.replaceState({}, "", redirectPath);
+          }
+        }
       },

Also applies to: 197-205

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useVercelChat.ts` around lines 189 - 196, The pending redirect handling
inside onToolCall currently only completes if refetchCredits() resolves, so a
rejected refetch can leave pendingRedirectPathRef.current set and prevent
navigation; update onToolCall to always perform the redirect navigation and
reset pendingRedirectPathRef.current regardless of refetchCredits() outcome by
moving the redirect nav/cleanup into a finally block or by catching errors from
refetchCredits() and then executing the redirect and clearing
pendingRedirectPathRef; refer to onToolCall, pendingRedirectPathRef, and
refetchCredits() in the change.

Comment on lines +11 to +18
const apiParam = new URLSearchParams(window.location.search).get("api");
if (apiParam === "clear") {
return defaultApiBaseUrl;
}

if (apiParam) {
return new URL(apiParam).toString().replace(/\/+$/, "");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Block arbitrary ?api= overrides from steering production traffic.

Accepting any absolute URL here lets a shared link reroute all chat and stream requests to an attacker-controlled origin. Because providers/ApiOverrideSync.tsx persists the same value and hooks/useChatTransport.ts Lines 15-25 uses it for request routing, this can leak prompts and any auth headers attached by callers. Please gate query-param overrides behind a trusted allowlist or a development-only switch before shipping this.

As per coding guidelines, **/*.{ts,tsx,js}: Implement built-in security practices for authentication and data handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/getClientApiBaseUrl.ts` around lines 11 - 18, The current
getClientApiBaseUrl logic allows any ?api= absolute URL which can redirect
production traffic; change it to only accept overrides if the origin is trusted
or when running in a non-production/dev mode. Specifically, update the code
around apiParam (in getClientApiBaseUrl) to: 1) parse apiParam and validate its
origin against a hardcoded allowlist of trusted origins or check a runtime
feature flag/ENV (e.g., NODE_ENV !== 'production' or an ALLOW_API_OVERRIDE flag)
before returning it; 2) if validation fails, fall back to defaultApiBaseUrl; and
3) keep the trimming of trailing slashes but do not accept raw hostnames/paths
without explicit allowlist/flag; also ensure any persistent use in
providers/ApiOverrideSync.tsx and hooks/useChatTransport.ts respects the same
validation function to avoid leaking requests to untrusted origins.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 8 files

Confidence score: 2/5

  • There are multiple concrete user-impact risks at severity 7/10, including a potential client-side request hijack path in lib/api/getClientApiBaseUrl.ts where ?api= can point requests at an untrusted origin.
  • components/VercelChat/ToolComponents.tsx can crash rendering if JSON.parse runs on empty/non-JSON tool output before the redirect branch, which makes this a likely runtime regression.
  • In hooks/useVercelChat.ts, the redirect flow is fragile (history.replaceState does not actually navigate) and the new tool-triggered redirect behavior shipped without a regression test, increasing the chance of breakage.
  • Pay close attention to hooks/useVercelChat.ts, lib/api/getClientApiBaseUrl.ts, and components/VercelChat/ToolComponents.tsx - navigation correctness, API-origin validation, and defensive parsing are the highest-risk areas.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="hooks/useVercelChat.ts">

<violation number="1" location="hooks/useVercelChat.ts:64">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

This behavior change (tool-triggered redirect via `onToolCall`/`onFinish`) ships without a regression test, which Rule 3 explicitly flags for bug-fix/behavior-change PRs.</violation>

<violation number="2" location="hooks/useVercelChat.ts:189">
P1: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**

This adds more behavior to an already far-over-limit hook file instead of splitting responsibilities; Rule 1 requires files to stay under 100 LOC.</violation>

<violation number="3" location="hooks/useVercelChat.ts:204">
P2: `history.replaceState` only changes the address bar, so the redirect tool won’t navigate to the new route and the chat will keep using the old `id`. Use a real navigation call (e.g., `window.location.replace` or the Next router) so the redirect actually takes effect.</violation>
</file>

<file name="lib/api/getClientApiBaseUrl.ts">

<violation number="1" location="lib/api/getClientApiBaseUrl.ts:17">
P1: Validate or restrict the `api` query param before using it as the API base URL. As written, any link can set `?api=...` to an arbitrary origin, which can redirect client API requests to an untrusted host and potentially leak auth headers or user data.</violation>
</file>

<file name="components/VercelChat/ToolComponents.tsx">

<violation number="1" location="components/VercelChat/ToolComponents.tsx:346">
P1: Handle the `redirect` case before parsing dynamic-tool output, or parse defensively. `JSON.parse` here can throw on empty/non-JSON output and crash rendering before the redirect branch runs.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

@@ -61,6 +61,7 @@ export function useVercelChat({
const { refetchCredits } = usePaymentProvider();
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Flag AI Slop and Fabricated Changes

This behavior change (tool-triggered redirect via onToolCall/onFinish) ships without a regression test, which Rule 3 explicitly flags for bug-fix/behavior-change PRs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useVercelChat.ts, line 64:

<comment>This behavior change (tool-triggered redirect via `onToolCall`/`onFinish`) ships without a regression test, which Rule 3 explicitly flags for bug-fix/behavior-change PRs.</comment>

<file context>
@@ -61,6 +61,7 @@ export function useVercelChat({
   const { refetchCredits } = usePaymentProvider();
   const { transport, getHeaders } = useChatTransport();
   const { authenticated } = usePrivy();
+  const pendingRedirectPathRef = useRef<string | null>(null);
 
   // Load artist files for mentions (from Supabase)
</file context>
Fix with Cubic

@@ -61,6 +61,7 @@ export function useVercelChat({
const { refetchCredits } = usePaymentProvider();
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Code Structure and Size Limits for Readability and Single Responsibility

This adds more behavior to an already far-over-limit hook file instead of splitting responsibilities; Rule 1 requires files to stay under 100 LOC.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useVercelChat.ts, line 189:

<comment>This adds more behavior to an already far-over-limit hook file instead of splitting responsibilities; Rule 1 requires files to stay under 100 LOC.</comment>

<file context>
@@ -181,12 +182,27 @@ export function useVercelChat({
         console.error("An error occurred, please try again!", e);
         toast.error("An error occurred, please try again!");
       },
+      onToolCall: async ({ toolCall }) => {
+        if ("toolName" in toolCall && toolCall.toolName === "redirect") {
+          const path = (toolCall.input as { path?: string } | undefined)?.path;
</file context>
Fix with Cubic

}

if (apiParam) {
return new URL(apiParam).toString().replace(/\/+$/, "");
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Validate or restrict the api query param before using it as the API base URL. As written, any link can set ?api=... to an arbitrary origin, which can redirect client API requests to an untrusted host and potentially leak auth headers or user data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/api/getClientApiBaseUrl.ts, line 17:

<comment>Validate or restrict the `api` query param before using it as the API base URL. As written, any link can set `?api=...` to an arbitrary origin, which can redirect client API requests to an untrusted host and potentially leak auth headers or user data.</comment>

<file context>
@@ -8,6 +8,15 @@ export function getClientApiBaseUrl(): string {
+      }
+
+      if (apiParam) {
+        return new URL(apiParam).toString().replace(/\/+$/, "");
+      }
+
</file context>
Suggested change
return new URL(apiParam).toString().replace(/\/+$/, "");
const parsedApiUrl = new URL(apiParam, defaultApiBaseUrl);
if (parsedApiUrl.origin !== new URL(defaultApiBaseUrl).origin) {
return defaultApiBaseUrl;
}
return parsedApiUrl.toString().replace(/\/+$/, "");
Fix with Cubic

Comment on lines 346 to 350
const result =
type === "dynamic-tool"
? JSON.parse((output as CallToolResult).content[0].text)
: output;
const toolName = getToolOrDynamicToolName(part);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Handle the redirect case before parsing dynamic-tool output, or parse defensively. JSON.parse here can throw on empty/non-JSON output and crash rendering before the redirect branch runs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/VercelChat/ToolComponents.tsx, line 346:

<comment>Handle the `redirect` case before parsing dynamic-tool output, or parse defensively. `JSON.parse` here can throw on empty/non-JSON output and crash rendering before the redirect branch runs.</comment>

<file context>
@@ -335,10 +343,10 @@ export function getToolCallComponent(part: ToolUIPart) {
-  const result = isMcp
-    ? JSON.parse((output as CallToolResult).content[0].text)
-    : output;
+  const result =
+    type === "dynamic-tool"
+      ? JSON.parse((output as CallToolResult).content[0].text)
</file context>
Suggested change
const result =
type === "dynamic-tool"
? JSON.parse((output as CallToolResult).content[0].text)
: output;
const toolName = getToolOrDynamicToolName(part);
const toolName = getToolOrDynamicToolName(part);
if (toolName === "redirect") {
return null;
}
const result =
type === "dynamic-tool"
? (() => {
const text = (output as CallToolResult)?.content?.[0]?.text;
if (!text) return output;
try {
return JSON.parse(text);
} catch {
return output;
}
})()
: output;
Fix with Cubic

if (pendingRedirectPathRef.current) {
const redirectPath = pendingRedirectPathRef.current;
pendingRedirectPathRef.current = null;
window.history.replaceState({}, "", redirectPath);
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: history.replaceState only changes the address bar, so the redirect tool won’t navigate to the new route and the chat will keep using the old id. Use a real navigation call (e.g., window.location.replace or the Next router) so the redirect actually takes effect.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useVercelChat.ts, line 204:

<comment>`history.replaceState` only changes the address bar, so the redirect tool won’t navigate to the new route and the chat will keep using the old `id`. Use a real navigation call (e.g., `window.location.replace` or the Next router) so the redirect actually takes effect.</comment>

<file context>
@@ -181,12 +182,27 @@ export function useVercelChat({
+        if (pendingRedirectPathRef.current) {
+          const redirectPath = pendingRedirectPathRef.current;
+          pendingRedirectPathRef.current = null;
+          window.history.replaceState({}, "", redirectPath);
+        }
       },
</file context>
Fix with Cubic

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="hooks/useChatTransport.ts">

<violation number="1">
P1: API base URL is frozen at render time, so request-time API override changes are not reliably applied to send/reconnect requests.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl";
import { usePrivy } from "@privy-io/react-auth";

export function useChatTransport() {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: API base URL is frozen at render time, so request-time API override changes are not reliably applied to send/reconnect requests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useChatTransport.ts, line 16:

<comment>API base URL is frozen at render time, so request-time API override changes are not reliably applied to send/reconnect requests.</comment>

<file context>
@@ -5,27 +5,17 @@ import { usePrivy } from "@privy-io/react-auth";
-        headers: request.headers,
-        credentials: request.credentials,
-      }),
+      api: `${baseUrl}/api/chat`,
     });
-  }, []);
</file context>
Suggested change
export function useChatTransport() {
api: `${baseUrl}/api/chat`,
prepareSendMessagesRequest: async (request) => ({
api: `${getClientApiBaseUrl()}/api/chat`,
headers: request.headers,
body: request.body ?? {},
credentials: request.credentials,
}),
prepareReconnectToStreamRequest: async (request) => ({
api: `${getClientApiBaseUrl()}/api/chat/${request.id}/stream`,
headers: request.headers,
credentials: request.credentials,
}),
Fix with Cubic

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 4 files (changes from recent commits).

Requires human review: Auto-approval blocked by 6 unresolved issues from previous reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant